from __future__ import annotations
from typing import Callable, Dict, List
from nicegui import ui
from nicegui.dataclasses import KWONLY_SLOTS
from nicegui.events import UiEventArguments, handle_event

from dataclasses import dataclass

_event_type_mark = "data-slot-mark"
_event_call_template = r"""() => $parent.$emit("{}", {{row:props.row,type:"{}"}})"""
_td_template = r"""<q-td key="{}" :props="props">{}</q-td>"""


@dataclass
class ElementInfo:
    tag: str
    props: dict
    children: List[ElementInfo]
    events: List[EventInfo]

    def to_template(self, table_event_name: str):
        props = " ".join([f"{k}='{v}'" for k, v in self.props.items()])
        children = "".join(
            [child.to_template(table_event_name) for child in self.children]
        )

        events = " ".join(
            [
                f"@{event.name}='{_event_call_template.format(table_event_name,event.callback_mark)}'"
                for event in self.events
            ]
        )

        return f"<{self.tag} {props} {events}>{children}</{self.tag}>"


@dataclass
class EventInfo:
    name: str
    callback_mark: str

    @classmethod
    def create(cls, element: ui.element) -> List[EventInfo]:
        listeners = list(
            listener.to_dict() for listener in element._event_listeners.values()
        )

        return [
            EventInfo(
                name=listener["type"],
                callback_mark=element.props.get(_event_type_mark, ""),
            )  # type: ignore
            for listener in listeners
        ]


def collect_element_data(element: ui.element):
    events = EventInfo.create(element)

    return ElementInfo(
        tag=element.tag,
        props=element.props,
        events=events,
        children=[collect_element_data(child) for child in element],
    )


@dataclass(**KWONLY_SLOTS)
class SlotTemplateEventArguments(UiEventArguments):
    row: Dict
    type: str


class SlotTemplate(ui.element):
    def __init__(
        self, column_name: str, event_fn: Callable[[SlotTemplateEventArguments], None]
    ) -> None:
        super().__init__("div")
        self.column_name = column_name
        self.event_fn = event_fn
        self.event_name = self.event_fn.__name__

    def collect_children_template(self):
        return "\n".join(
            [collect_element_data(child).to_template(self.event_name) for child in self]
        )

    def to_template(self):
        return _td_template.format(self.column_name, self.collect_children_template())

    def apply_table(self, table: ui.table):
        slot_str = self.to_template()
        table.add_slot(f"body-cell-{self.column_name}", slot_str)

        def on_table_event(e):
            args = e.args
            handle_event(
                self.event_fn,
                SlotTemplateEventArguments(
                    sender=e.sender, client=e.client, row=args["row"], type=args["type"]
                ),
            )

        table.on(self.event_name, on_table_event)
        self.delete()

    def mark(self, mark: str):
        return f'{_event_type_mark}="{mark}"'


# example usage
if __name__ in {"__main__", "__mp_main__"}:
    columns = [
        {"name": "name", "label": "Name", "field": "name"},
        {"name": "age", "label": "Age", "field": "age"},
    ]
    rows = [
        {"id": 0, "name": "Alice", "age": 18},
        {"id": 1, "name": "Bob", "age": 21},
        {"id": 2, "name": "Carol"},
    ]
    name_options = ["Alice", "Bob", "Carol"]

    table = ui.table(columns=columns, rows=rows).classes("w-full")

    def on_click(e: SlotTemplateEventArguments):
        if e.type == "view":
            ui.notify(f"点击了查看按钮，数据为：{e.row}")
        elif e.type == "edit":
            ui.notify(f"点击了编辑按钮，数据为：{e.row}")
        elif e.type == "delete":
            ui.notify(f"点击了删除按钮，数据为：{e.row}")

    with SlotTemplate("name", on_click) as st:
        with ui.row():
            ui.button("查看", color="blue-500", on_click=lambda: None).props(
                st.mark("view")
            )
            ui.button("编辑", color="blue-500", on_click=lambda: None).props(
                st.mark("edit")
            )
            ui.button("删除", color="blue-500", on_click=lambda: None).props(
                st.mark("delete")
            )

    st.apply_table(table)

    ui.run()
